/**
 ********************************************************************
 * @author roocket
 * @file    video.c
 * @version V0.0.0
 * @date    2024/3/22
 * @brief   This file is to indicate video functions. 
 * @attention Code file rules:
 * rule: file encoding use UTF8;
 * rule: max line length 120 characters;
 * rule: line separator \r\n;
 * rule: use clion auto code format tool.
 */
#include "video.h"

int video_init(const char *video, struct video_compatible_info *vfo)
{
    int fd;
    fd = open(video, O_RDWR);
    if (fd < 0) {
        perror(open video failed");
        return -1;"
    }

    struct v4l2_format v_fmt;
    v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v_fmt.fmt.pix.width = vfo->width;
    v_fmt.fmt.pix.height = vfo->height;
    v_fmt.fmt.pix.pixelformat = vfo->pixel_format;

    int ret = ioctl(fd, VIDIOC_S_FMT, &v_fmt);
    if (ret < 0) {
        perror("set frame failed");
        return -1;
    }

    memset(&v_fmt, 0, sizeof(v_fmt));
    v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd, VIDIOC_G_FMT, &v_fmt);	
    if (ret < 0) {
        perror("failed to get started");
        return -1;
    }
    printf("width = %d\n", v_fmt.fmt.pix.width);
    printf("width = %d\n", v_fmt.fmt.pix.height);
    unsigned char *p = (unsigned char*)&v_fmt.fmt.pix.pixelformat;
    printf("pixelformat = %c%c%c%c\n", p[0],p[1],p[2],p[3]);	

    struct v4l2_requestbuffers req_buffer;
    req_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req_buffer.count = QUEUE_MAX_NUM;
    req_buffer.memory = V4L2_MEMORY_MMAP;

    ret = ioctl(fd, VIDIOC_REQBUFS, &req_buffer);
    if(ret < 0) {
        perror("alloc frame failed");
        return -1;
    }

    struct v4l2_buffer v_buffer;
    v_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    for (int i = 0; i < QUEUE_MAX_NUM; i++) {
        v_buffer.index = i;
        ret = ioctl(fd, VIDIOC_QUERYBUF, &v_buffer);
        if(ret < 0)
            perror("ioctl");
        vfo->mmap_addr[i] = (unsigned char *)mmap(NULL, v_buffer.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, v_buffer.m.offset);
        vfo->addr_length[i] = v_buffer.length;

        ret = ioctl(fd, VIDIOC_QBUF, &v_buffer);
        if(ret < 0)
            perror("ioctl failed");
    }

    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd, VIDIOC_STREAMON, &type);
    if(ret < 0)
        perror("open video device");

    return fd;
}

void video_be_capturing(struct video_compatible_info *vfo)
{
    vfo->vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}

static int video_get_one_image(const char *dir, int fd, struct video_compatible_info *vfo, unsigned short index)
{
    int ret = -1;
    char image[128] = "";

    memset(image, 0x00, 128);

    ret = ioctl(fd, VIDIOC_DQBUF, &vfo->vb);
    if (ret < 0)
        return -1;

    snprintf(image, 128, "%s/%d.jpg", dir, index);

    FILE *file = fopen(image, "w+");
    fwrite(vfo->mmap_addr[vfo->vb.index], vfo->vb.length, 1, file);
    fclose(file);

    ret = ioctl(fd, VIDIOC_QBUF, &vfo->vb);
    if (ret < 0)
        return -1;
    return 0;
}

int video_get_bulk_images(const char *dir, int fd, struct video_compatible_info *ci, unsigned short bulks)
{
    unsigned short i = 0;
    int ret;

    for (; i < bulks; i++) {
        ret = video_get_one_image(dir, fd, ci, i);
        if (-1 == ret)
            break;
        ret = 0;
    }
    return ret;
}

int video_del(int fd, struct video_compatible_info *vfo)
{
    int ret = -1;
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
    if(ret < 0)
        perror("close dev");
    for(int i = 0; i < QUEUE_MAX_NUM; i++)
        munmap(vfo->mmap_addr[i], vfo->addr_length[i]);
    close(fd);
    return 0;
}

void video_get_data(void)
{
    int fd = -1;
    int ret = -1;

    struct video_compatible_info vfo = {0};

    vfo.width = 480;
    vfo.height = 640;
    vfo.pixel_format = V4L2_PIX_FMT_MJPEG;

    fd = video_init("/dev/video20", &vfo);
    if (-1 == fd) {
        perror("fail init video");
        return;
    }

    video_be_capturing(&vfo);

#if 0
    video_get_one_image("/tmp", fd, &vfo, 0);
#endif

    ret = video_get_bulk_images("/tmp", fd, &vfo, 300);
    if (-1 == ret) {
        printf("get bulk images fail\n");
        goto free_vd;
    }

free_vd:
    video_del(fd, &vfo);
    return;
}
